package com.hbx.auth;

import com.hbx.auth.exception.AuthException;
import com.hbx.auth.jwt.JwtUser;
import com.hbx.auth.jwt.JwtUtil;
import com.hbx.constant.AuthContent;
import com.hbx.constant.CachePrefixContent;
import com.hbx.constant.RspEnum;
import com.hbx.model.User;
import com.hbx.service.UserService;
import com.hbx.utils.CacheUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * @Author:hbx
 *
 * */
public class AuthInceptor implements HandlerInterceptor {

    private static Logger log = LoggerFactory.getLogger(AuthInceptor.class);

    @Value("${auth.expires:1800000}")
    public Long expires;

    @Value("${auth.scriect:1174586985@22.com}")
    public String scricet;

    @Autowired
    private UserService userService;

    @Autowired
    private CacheUtils cacheUtils;

    /**
     * 前置处理
     * @param request
     * @param response
     * @param handler
     * @return
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("start handler request");
        User user = getUserInfo(request);
        Token token = getUserToken(request);
        if (ObjectUtils.isEmpty(user) || ObjectUtils.isEmpty(token)){
            log.error("【身份认证】：用户身份验证失败,user:{},token:{}", user, token);
            throw new AuthException(RspEnum.AUTH_NO_LOGIN);
        }
        // 重新生成token
        String newToken = JwtUtil.generateToken(new JwtUser(user.getUserId(), user.getLoginName()), scricet, expires);
        // 刷新过期时间
        log.info("【身份认证】:身份认证通过,刷新有效时间");
        cacheUtils.set(CachePrefixContent.TOKEN_PREFIX + token.getKey(), newToken, expires);
        AuthCacheService.setUserThreadLocal(user);
        AuthCacheService.setTokenThreadLocal(token);
        return true;
    }

    /**
     * 最终处理
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AuthCacheService.removeUser();
        AuthCacheService.removeToken();
    }

    /**
     * 根据token获得用户信息
     * @param request
     * @return
     */
    private User getUserInfo(HttpServletRequest request){
        String header = request.getHeader(AuthContent.ACCESS_TOKEN);
        if (!StringUtils.hasLength(header)){
            log.error("【身份认证】：用户认证失败,Access-Token为空");
            throw new AuthException(RspEnum.AUTH_ACCESS_TOKEN_IS_NULL);
        }
        String token = cacheUtils.get(CachePrefixContent.TOKEN_PREFIX + header);
        if (!StringUtils.hasLength(token)){
            log.error("【身份认证】：用户认证失败,令牌已过期");
            throw new AuthException(RspEnum.AUTH_NO_LOGIN);
        }
        // 判断是否过期
        if (JwtUtil.isTokenExpire(token)){
            log.error("【身份认证】：用户认证失败,Access-Token已经过期");
            throw new AuthException(RspEnum.AUTH_NO_LOGIN);
        }
        //  解析token
        JwtUser jwtUser = JwtUtil.getClaimInfo(token);
        log.info("【身份认证】：Access-Token验证通过,用户载荷,{}",jwtUser);
        if (jwtUser != null){
            return userService.getById(jwtUser.getId());
        }
        return null;
    }

    /**
     * 获得token
     * @param request
     * @return
     */
    private Token getUserToken(HttpServletRequest request){
        String header = request.getHeader(AuthContent.ACCESS_TOKEN);
        if (!StringUtils.hasLength(header)){
            throw new AuthException(RspEnum.AUTH_ACCESS_TOKEN_IS_NULL);
        }
        String tokenStr = cacheUtils.get(CachePrefixContent.TOKEN_PREFIX + header);
        if (!StringUtils.hasLength(tokenStr)){
            log.error("【身份认证】：用户认证失败,令牌已过期");
            throw new AuthException(RspEnum.AUTH_NO_LOGIN);
        }
        Token token = new Token();
        token.setKey(header);
        token.setToken(tokenStr);
        return token;
    }
}
